﻿using AChance.Bll.Trans;
using AChance.Dal.Func;
using AChance.Func;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;

namespace AChance.Bll.Func
{
    public static class BllTrans
    {
        /// <summary>
        /// 状态
        /// </summary>
        public static class State
        {
            /// <summary>
            /// 执行成功
            /// </summary>
            public const string SUCCESS = "0000";
            /// <summary>
            /// 执行失败
            /// </summary>
            public const string FAIL = "9999";
            /// <summary>
            /// 获取描述
            /// </summary>
            public static string GetDesc(string state) =>
                state switch
                {
                    SUCCESS => "执行成功",
                    _ => "执行失败"
                };
        }
        /// <summary>
        /// 交易码
        /// </summary>
        public static class Code
        {
            /// <summary>
            /// 交易码类型
            /// </summary>
            public static class Type
            {
                public const string GET = ".Get";
                public const string ADD = ".Add";
                public const string CHG = ".Chg";
                public const string DEL = ".Del";
            }
            /// <summary>
            /// 提取交易码
            /// </summary>
            public static string Extract<T>() where T : class
            {
                System.Type transType = typeof(T);
                string transCode = transType.ReflectedType == null ? transType.Name : transType.ReflectedType.Name;
                transCode += ".";
                transCode += transType.ReflectedType == null ? string.Empty : transType.Name;
                return transCode;
            }
            /// <summary>
            /// 校验交易码格式
            /// </summary>
            public static bool CheckFormat(string transCode)
            {
                return !string.IsNullOrWhiteSpace(transCode) && transCode.Contains(".");
            }

            #region 交易码校验策略
            /// <summary>
            /// 交易码校验策略
            /// </summary>
            public class CheckStrategy
            {
                /// <summary>
                /// 空的情况下包含所有交易码
                /// </summary>
                public List<string> Include { get; set; } = new List<string>();
                /// <summary>
                /// 空的情况下不包含任何交易码
                /// </summary>
                public List<string> Exclude { get; set; } = new List<string>();
                /// <summary>
                /// 空的情况下包含所有交易码
                /// </summary>
                public List<string> NeedSession { get; set; } = new List<string>();
                /// <summary>
                /// 空的情况下不包含任何交易码
                /// </summary>
                public List<string> NoNeedSession { get; set; } = new List<string>();
                public bool IsInclude(string transCode)
                {
                    if (!CheckFormat(transCode))
                        return false;
                    if (Include.Count > 0)
                        if (!Include.Exists(strategy => transCode.Contains(strategy)))
                            return false;
                    if (Exclude.Count > 0)
                        if (Exclude.Exists(strategy => transCode.Contains(strategy)))
                            return false;
                    return true;
                }
                public bool IsNeedSession(string transCode)
                {
                    if (!IsInclude(transCode))
                        return true;
                    if (NeedSession.Count > 0)
                        if (!NeedSession.Exists(strategy => transCode.Contains(strategy)))
                            return false;
                    if (NoNeedSession.Count > 0)
                        if (NoNeedSession.Exists(strategy => transCode.Contains(strategy)))
                            return false;
                    return true;
                }
            }
            #endregion
        }
        /// <summary>
        /// 当前线程的交易操作实例
        /// </summary>
        private static TransOper Oper
        {
            get
            {
                return (TransOper)FunTask.Context.TransOper.GetOrAdd(nameof(TransOper), new TransOper());
            }
        }
        /// <summary>
        /// 交易操作
        /// </summary>
        private class TransOper
        {
            /// <summary>
            /// 交易包
            /// </summary>
            internal TransPkg Pkg { get; private set; }

            #region 开关
            /// <summary>
            /// 打开交易操作
            /// </summary>
            public void Open(TransPkg transPkg)
            {
                if (Pkg != null)
                    throw new Exception("当前交易操作还未关闭，无法再次打开");
                Pkg = transPkg;
            }
            /// <summary>
            /// 关闭交易操作
            /// </summary>
            public void Close()
            {
                Pkg = null;
            }
            #endregion
        }
        /// <summary>
        /// 当前线程的交易操作实例中交易包的传入状态信息
        /// </summary>
        internal static BllVar.StateInfo InStateInfo
        {
            get
            {
                return Oper.Pkg.InStateInfo;
            }
        }
        /// <summary>
        /// 当前线程的交易操作实例中交易包的传出信息详情
        /// </summary>
        internal static List<string> OutMsgDetail
        {
            get
            {
                return Oper.Pkg.OutMsgDetail;
            }
        }
        /// <summary>
        /// 交易包
        /// </summary>
        [Serializable]
        public class TransPkg : FunVar.DataPkg
        {
            protected string _inTransCode = "";
            protected BllVar.StateInfo _inStateInfo = new BllVar.StateInfo();
            protected object _inDataObj = null;

            protected object _outDataObj = null;

            /// <summary>
            /// 传入交易码
            /// </summary>
            public string InTransCode
            {
                get { return _inTransCode; }
                set { _inTransCode = value?.Trim() ?? ""; }
            }
            /// <summary>
            /// 传入状态信息
            /// </summary>
            [JsonIgnore]
            public BllVar.StateInfo InStateInfo
            {
                get { return _inStateInfo; }
                set { _inStateInfo = value; }
            }
            /// <summary>
            /// 传入数据对象
            /// </summary>
            internal object InDataObj
            {
                get { return _inDataObj; }
                set { _inDataObj = value; }
            }
            /// <summary>
            /// 传出数据对象
            /// </summary>
            internal object OutDataObj
            {
                get { return _outDataObj; }
                set { _outDataObj = value; }
            }
            /// <summary>
            /// 判断执行是否成功
            /// </summary>
            public bool IsExecSuccess()
            {
                return OutState.Equals(State.SUCCESS);
            }
            /// <summary>
            /// 获取执行失败信息
            /// </summary>
            public string GetExecFailMsg()
            {
                return OutMsg;
            }
            /// <summary>
            /// 获取执行情况信息
            /// </summary>                        
            public string GetExecCircsMsg()
            {
                return ((OutMsg.IsNullOrEmpty() ? State.GetDesc(OutState) : _outMsg) + " {" + InTransCode + "}").ToWebContent();
            }
            /// <summary>
            /// 清空所有数据
            /// </summary>
            public override void Clear()
            {
                ClearIn();
                ClearOut();
            }
            /// <summary>
            /// 清空所有传入数据
            /// </summary>
            public override void ClearIn()
            {
                InTransCode = "";
                InStateInfo = new BllVar.StateInfo();
                InData = new Hashtable();
                InDataObj = null;
            }
            /// <summary>
            /// 清空所有传出数据
            /// </summary>
            public override void ClearOut()
            {
                OutState = "";
                OutMsg = "";
                OutMsgDetail = new List<string>();
                OutData = new Hashtable();
                OutDataObj = null;
            }
        }
        /// <summary>
        /// 获取特性
        /// </summary>
        private static BllTransAttribute GetAttr(Type transType)
        {
            return (BllTransAttribute)transType.GetCustomAttribute(typeof(BllTransAttribute)) ?? new BllTransAttribute();
        }
        /// <summary>
        /// 准备数据对象
        /// </summary>
        private static void PrepDataObj(Type transType, TransPkg transPkg)
        {
            var inDataType = transType.GetNestedType(nameof(TransPkg.InData));
            if (inDataType != null)
                transPkg.InDataObj = Activator.CreateInstance(inDataType);
            var outDataType = transType.GetNestedType(nameof(TransPkg.OutData));
            if (outDataType != null)
                transPkg.OutDataObj = Activator.CreateInstance(outDataType);
        }
        /// <summary>
        /// 准备传入数据
        /// </summary>
        private static void PrepInData(TransPkg transPkg)
        {
            var dataObj = transPkg.InDataObj;
            if (dataObj == null)
                return;
            foreach (PropertyInfo propertyInfo in dataObj.GetType().GetProperties())
            {
                var attr = (BllDataAttribute)propertyInfo.GetCustomAttribute(typeof(BllDataAttribute));
                if (attr != null)
                {
                    var value = transPkg.InData[propertyInfo.Name];
                    if (value is null)
                    {
                        if (attr.Need)
                            throw new Exception($"传入数据中缺少({attr.DispName})数据");
                    }
                    else
                    {
                        try
                        {
                            if (value.GetType().Equals(propertyInfo.PropertyType))
                                value = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(value), value.GetType(),
                                    new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace });
                            else if (propertyInfo.PropertyType == typeof(string))
                                value = Convert.ToString(value);
                            else if (propertyInfo.PropertyType == typeof(int))
                                value = Convert.ToInt32(value);
                            else if (propertyInfo.PropertyType == typeof(double))
                                value = Convert.ToDouble(value);
                            else if (value.GetType() == typeof(JObject))
                                value = ((JObject)value).ToObject(propertyInfo.PropertyType);
                            else if (value.GetType() == typeof(JArray))
                                value = ((JArray)value).ToObject(propertyInfo.PropertyType);
                            else
                                throw new Exception();
                            propertyInfo.SetValue(dataObj, value);
                            transPkg.InData[propertyInfo.Name] = value;
                        }
                        catch
                        {
                            throw new Exception($"传入数据中的({attr.DispName})数据类型解析失败");
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 准备传出数据
        /// </summary>
        private static void PrepOutData(TransPkg transPkg)
        {
            var dataObj = transPkg.OutDataObj;
            if (dataObj is null)
                return;
            foreach (PropertyInfo propertyInfo in dataObj.GetType().GetProperties())
            {
                var attr = (BllDataAttribute)propertyInfo.GetCustomAttribute(typeof(BllDataAttribute));
                if (attr != null)
                {
                    transPkg.OutData[propertyInfo.Name] = propertyInfo.GetValue(dataObj);
                }
            }
        }
        /// <summary>
        /// 调用交易
        /// </summary>
        public static bool Invoke<T>(string execFailMsg = "") where T : class
        {
            var transPkg = new TransPkg
            {
                InTransCode = Code.Extract<T>()
            };
            return Invoke(transPkg, execFailMsg);
        }
        /// <summary>
        /// 调用交易
        /// </summary>
        public static bool Invoke<T>(TransPkg transPkg, string execFailMsg = "") where T : class
        {
            transPkg.InTransCode = Code.Extract<T>();
            return Invoke(transPkg, execFailMsg);
        }
        /// <summary>
        /// 调用交易
        /// </summary>
        public static bool Invoke(TransPkg transPkg, string execFailMsg = "")
        {
            //检查数据包是否有效
            if (transPkg is null)
                throw new Exception("数据包无效");
            //开始计时
            Stopwatch stopwatch = Stopwatch.StartNew();
            try
            {
                //打开交易操作
                Oper.Open(transPkg);
                //清空所有传出数据
                transPkg.ClearOut();
                //检查交易是否存在
                Type transType = null;
                MethodInfo methodInfo = null;
                try
                {
                    string[] transCode = transPkg.InTransCode.Split('.');
                    transType = Assembly.GetExecutingAssembly().GetType($"{typeof(TrnSql).Namespace}.{transCode[0]}").GetNestedType(transCode[1]);
                    methodInfo = FunIdv.GetInfo(transType, nameof(TrnSql.ChgStruct.Exec));
                }
                catch
                {
                    throw new Exception("交易不存在");
                }
                //准备数据对象
                PrepDataObj(transType, transPkg);
                //准备传入数据
                PrepInData(transPkg);
                //获取交易特性
                var transAttr = GetAttr(transType);
                try
                {
                    //判断是否需要加锁
                    FunLock.Lock(transAttr.LockMode, transPkg.InTransCode, transAttr.LockExpiry, transAttr.LockWait, transAttr.LockRetry, "交易加锁失败");
                    //判断是否操作数据库
                    if (transAttr.SqlOperMode != DalConst.SqlOperMode.None)
                    {
                        //打开数据库操作
                        DalOper.Sql.Open();
                        //判断是否开启事务
                        if (transAttr.SqlOperMode != DalConst.SqlOperMode.ReadOnly)
                            DalOper.Sql.BeginTran();
                    }
                    //调用交易
                    var args = new List<object>();
                    if (transPkg.InDataObj != null)
                        args.Add(transPkg.InDataObj);
                    if (transPkg.OutDataObj != null)
                        args.Add(transPkg.OutDataObj);
                    methodInfo.Invoke(null, args.ToArray());
                    //提交事务并关闭数据库操作
                    DalOper.Sql.Close(true);
                }
                catch (Exception ex)
                {
                    //回滚事务并关闭数据库操作
                    DalOper.Sql.Close();
                    FunLogs.LogErrorForTransExec(transPkg.InTransCode, ex.InnerException ?? ex);
                    throw new Exception(ex.InnerException?.Message ?? ex.Message);
                }
                finally
                {
                    //判断是否需要解锁
                    FunLock.Unlock(transAttr.LockMode, transPkg.InTransCode);
                }
                //准备传出数据
                PrepOutData(transPkg);
                transPkg.OutState = State.SUCCESS;
                return true;
            }
            catch (Exception ex)
            {
                transPkg.OutState = State.FAIL;
                transPkg.OutMsg = ex.Message;
                return false;
            }
            finally
            {
                //关闭交易操作
                Oper.Close();
                //结束计时
                stopwatch.Stop();
                FunLogs.LogWarnForTransExec(transPkg.InTransCode, stopwatch.Elapsed, true);
                //记录交易执行信息
                if (FunContext.AppSettings.Console.RecordTransExecInfo)
                {
                    try
                    {
                        var execInfo = FunConsole.TransExecInfos.Find(execInfo => execInfo.Name.Equals(transPkg.InTransCode));
                        if (execInfo is null)
                            FunConsole.TransExecInfos.Add(execInfo = new FunConsole.Var.TransExecInfo { Name = transPkg.InTransCode });
                        execInfo.Elapsed = stopwatch.Elapsed.TotalMilliseconds;
                        execInfo.TotalNumber++;
                        execInfo.TotalTime += execInfo.Elapsed;
                        if (transPkg.IsExecSuccess())
                        {
                            execInfo.Success++;
                            execInfo.SuccessTime += execInfo.Elapsed;
                        }
                        else
                        {
                            execInfo.Fail++;
                            execInfo.FailTime += execInfo.Elapsed;
                            execInfo.FailMsg = transPkg.GetExecFailMsg();
                        }
                    }
                    catch
                    {
                        FunConsole.TransExecInfos.Clear();
                    }
                }
                //执行失败后的消息处理
                if (!transPkg.IsExecSuccess() && execFailMsg != null)
                    throw new Exception(execFailMsg.IsEmpty() ? transPkg.GetExecFailMsg() : execFailMsg);
            }
        }
    }
}
